package com.asurplus.common.websocket;

import cn.hutool.core.collection.CollectionUtil;
import com.asurplus.common.consts.SystemConst;
import com.asurplus.system.entity.SysUserInfo;
import com.asurplus.websocket.service.WebsocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * WebSocket服务端
 *
 * @author lizhou
 */
@Slf4j
@ServerEndpoint(value = "/websocket", configurator = HttpSessionConfig.class)
@Component
public class WebSocketUtil {

    public static WebsocketService websocketService;

    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
     */
    public static Map<Integer, WebSocketUtil> webSocketSet = new HashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        SysUserInfo sysUserInfo = (SysUserInfo) httpSession.getAttribute(SystemConst.SYSTEM_USER_SESSION);
        if (null != sysUserInfo) {
            this.session = session;
            webSocketSet.put(sysUserInfo.getId(), this);
            // 在线数加1
            addOnlineCount();
            // 给当前用户发消息
            WebsocketVO websocketVO = new WebsocketVO();
            websocketVO.setReceiveUser(sysUserInfo.getId());
            websocketVO.setContent("连接WebSocket服务器成功！");
            sendMessage(websocketVO, sysUserInfo.getId());
            log.info("{} 连接Websocket！当前在线人数为{}", sysUserInfo.getName(), getOnlineCount());
        } else {
            log.info("用户没有登录,不能连接Websocket!");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        Integer key = null;
        if (CollectionUtil.isNotEmpty(webSocketSet)) {
            for (Integer getKey : webSocketSet.keySet()) {
                if (webSocketSet.get(getKey).session == this.session) {
                    key = getKey;
                }
            }
        }
        if (null != key) {
            // 从map中删除
            webSocketSet.remove(key);
            // 在线数减1
            subOnlineCount();
            log.info("有一连接关闭！当前在线人数为{}", getOnlineCount());
        } else {
            log.info("onClose()关WebSocket异常!");
        }
    }

    /**
     * 接收到消息
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("来自客户端的消息:{}", message);
        if (this.session.isOpen()) {
            this.session.getAsyncRemote().sendText(message);
        } else {
            log.info("当前用户session已关闭!");
        }
    }

    /**
     * 根据用户id发送消息
     *
     * @param userId
     * @throws IOException
     */
    public static void sendMessage(WebsocketVO websocketVO, Integer userId) {
        if (CollectionUtil.isNotEmpty(webSocketSet)) {
            for (Integer key : webSocketSet.keySet()) {
                // 找到指定用户
                if (key.equals(userId)) {
                    WebSocketUtil mws = webSocketSet.get(key);
                    if (null != mws) {
                        mws.session.getAsyncRemote().sendText(websocketVO.getContent());
                    }
                }
            }
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误");
        error.printStackTrace();
    }

    /**
     * 服务端给所有客户端发送消息
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        log.info("来自客户端的消息:{}", message);
        if (this.session.isOpen()) {
            this.session.getAsyncRemote().sendText(message);
        } else {
            log.info("当前用户session已关闭!");
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketUtil.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketUtil.onlineCount--;
    }
}